/**
 *  简单的html页面自动赋值工具
 *  使用方式，页面要自动赋值的地方，需要添加【auto】属性，属性的值即为要赋值的对象的值
 *  例子：
 *      <span>剩余<i>11</i>天</span>
 *      如果想动态变更剩余天数,假设该属性的属性名为day，应该将html进行如下修改
 *      <span>剩余<i auto="day">11</i>天</span>
 *      然后调用js即可
 *      autoJs.assignment(obj)
 *
 *  1. Html替换规则说明：
 *      <a>     会将auto的配置作为参数，拼接到href后，目前只支持单参数
 *      <img>   赋值src
 *      <input><textarea>   赋值val
 *      其他     赋值html
 *
 *  2. 特殊情况使用说明：
 *      2.1 弹框消息需要将关闭的按钮绑定 【auto="_close_btn"】, 才会给弹框自动绑定隐藏函数；
 *          当然也可以在指定按钮上添加onclick事件，但不支持js绑定，因为加载顺序的问题
 *
 *      2.2 需格式化时间字段时，需要在html中增加该属性，格式可以自定义
 *          【date-format="yyyy-MM-dd"】
 *
 *      2.3 显示隐藏, 【auto-show="stock_lt_0"】  --->>>  stock > 0 时显示
 *              eq 就是 equal等于
 *              ne 就是 not equal不等于
 *              gt 就是 greater than大于　
 *              lt 就是 less than小于
 *              ge 就是 greater than or equal 大于等于
 *              le 就是 less than or equal 小于等于
 *          2.3.1 多添建筛选的部分支持
 *              支持与操作，用&表示，例：【auto-show="statusId_eq_1&goodType_eq_2"】
 *          2.3.2 添加显示分组，加快渲染（※※※ 功能有问题，暂时废弃 ※※※）
 *              auto-show-group="btn"   btn只是个名字，只要被相同值的auto-show-group属性修饰，即可
 *
 *       2.4 【auto-click】 自动绑定点击事件
 *          此字段需要传递函数，而不是字符串；且函数的默认参数为此html替换内容的data对象数据
 *
 *       2.5 【auto-bind="objKey_attr"】 自动把属性objKey的值，绑定在html的attr标签上
 *
 *
 *  @version 1.0.0
 *  @since 2019年6月20日
 *  @author SumKids
 */
window.autoJs = {

    /**
     * 1. 普通赋值
     *
     * @param obj   要赋值的具体键值对
     * @param $element  可选参数，限定赋值的范围，默认为body
     * @returns {jQuery|HTMLElement|*} 只在$element不为body时返回
     */
    assignment: function (obj, $element) {
        $element = !$element ? $('body') : $element;
        // 【auto】属性自动处理
        let handleArr = $element.find('[auto]');
        if ($element.attr('auto'))
            handleArr.push($element)
        $.each(handleArr, function (i, n) {
            const _val = $(n).attr('auto');
            autoJs.inner.log('自动赋值属性：' + _val)
            if (_val.startsWith('_')) {
                autoJs.inner.specialHandle(_val,  $(n), $element);
            } else {
                try {
                    let val;
                    if (!obj) {
                        val = ''
                    } else {
                        val = Object.getOwnPropertyDescriptor(obj, _val).value;
                        if (typeof val == 'string' && val.indexOf('${request.contextPath}') !== -1) {
                            // autoJs.inner.log('自动赋值前：' + val + "," + autoJs.getHost())
                            val = val.replace(/\${request.contextPath}/g, autoJs.getHost())
                        }
                        if ($(n).attr('date-format')) {
                            val = autoJs.utils.dateFormat($(n).attr('date-format'), new Date(val))
                        }
                        autoJs.inner.log('自动赋值结果：' + val)
                    }
                    if ($(n).is('img')) {
                        if (val)
                            $(n).attr('src', val)
                    } else if ($(n).is('input') || $(n).is('textarea')) {
                        $(n).val(val)
                    } else if ($(n).is('a')) {  // a标签的auto表示传递参数
                        $(n).attr('href', $(n).attr('href') + '?' + _val + '=' + val)
                    } else
                        $(n).html(val)
                } catch (e) {
                    console.log('××××× 【auto】属性自动处理异常:', $(n).attr('auto'))
                    console.log(e)
                }
            }
        });
        // 【auto-show】属性自动处理
        handleArr = $element.find('[auto-show]');
        if ($element.attr('auto-show'))
            handleArr.push($element)
        $.each(handleArr, function (i, n) {
            let show = true;
            let opArr = $(n).attr('auto-show').split('&');
            // let showMap = new MapBuilder();
            try {
                // let key = $(n).attr('auto-show-group')
                for (let l = 0; l < opArr.length; l++) {
                    // if (key) {
                    //     if (showMap.get(key)) {
                    //         $(n).hide();
                    //         console.log('已经显示一条，此条隐藏')
                    //         continue;
                    //
                    //     }
                    // }
                    const arr = opArr[l].split('_');
                        if (arr.length === 3) {
                            let attr = arr[0];
                            let objVal = obj[attr];
                            let operator = arr[1];
                            let val = arr[2];

                            if ((operator === 'eq' && objVal != val) || (operator === 'ne' && objVal == val)
                                || (operator === 'gt' && objVal <= val) || (operator === 'lt' && objVal >= val)
                                || (operator === 'ge' && objVal < val) || (operator === 'le' && objVal > val)) {
                                $(n).hide();
                                show = false;
                                continue;
                            } else {
                                // $(n).show();
                            }
                        } else
                            console.log('配置auto-show异常:', $(n).attr('auto-show'))
                }
                if (show) {
                    // showMap.put(key, true)
                    $(n).show();
                }
            } catch (e) {
                console.log('××××× 【auto-show】属性自动处理异常:', $(n).attr('auto-show'))
                console.log(e)
            }

        });

        // 【auto-show】属性自动处理
        handleArr = $element.find('[auto-click]');
        if ($element.attr('auto-click'))
            handleArr.push($element)
        $.each(handleArr, function (i, n) {
            var func = $(n).attr('auto-click');
            try {
                $(n).on('click', function(){eval(func+'(obj)')})
            } catch (e) {
                console.log('××××× 【auto-click】属性自动处理异常:', $(n).attr('auto-click'))
                console.log(e)
            }
        })
        // 【auto-bind】属性自动处理
        handleArr = $element.find('[auto-bind]');
        if ($element.attr('auto-bind'))
            handleArr.push($element)
        $.each(handleArr, function (i, n) {
            let arr = $(n).attr('auto-bind').split('_');
            try {
                if (arr.length !== 2)
                    return
                $(n).attr(arr[1], obj[arr[0]])
            } catch (e) {
                console.log('××××× 【auto-bind】属性自动处理异常:', $(n).attr('auto-click'))
                console.log(e)
            }
        })
        if (!$element.is('body'))
            return $element;
    },
    /**
     * 2. 集合元素类型的html拼接
     *
     * @param html  样板html，※※※ 不要忘记增加auto属性 ※※※
     * @param arr   数据集合
     * @param $ele  代码拼接在哪个元素里
     * @param isClear   可选：是否清除元素内部原有元素，默认不清除
     */
    appendHtml: function (html, arr, $ele, isClear) {
        if (!html || !arr || arr.length === 0)
            return;
        if (isClear)
            $ele.html('')
        for (let i = 0; i < arr.length; i++) {
            $ele.append(this.assignment(arr[i], $(html)))
        }
    },
    /**
     * 3. 某些数据需要先转义，再进行自动替换
     * @param data  要转义的数据对象
     * @param attr  data中要替换的属性名
     * @param dict  转义字典
     */
    formatData: function (data, attr, dict) {
        let val;
        if (data instanceof Array) {
            for(let i in data) {
                // var _obj = data[i];
                data[i][attr] = dict[data[i][attr]]
                // eval("info."+name+"=" + idValue));
            }
        } else {
            val = data[attr]
            data[attr] = dict[val]
        }
    },
    /**
     * 4. 绑定下拉加载事件
     * @param func  下拉加载数据的函数
     * @param params    参数对象
     * @param preHeight     如果想距离底部50px就执行刷新，此字段传50
     */
    bindScrollFunc: function (func, params) {
        //滚动事件触发
        window.onscroll = function() {
            // console.log(autoJs.utils.getScrollTop() + autoJs.utils.getClientHeight(), autoJs.utils.getScrollHeight())
            if(autoJs.utils.getScrollTop() + autoJs.utils.getClientHeight() === autoJs.utils.getScrollHeight()) {
                console.log('下拉刷新了')
                if (func)
                    func(params)
            }
        }
    },
    /**
     * toast消息弹框
     * @param msg
     * @param delay 可选：自动消失时间，默认2000
     */
    toast: function (msg, delay) {
        let dom = document.getElementsByClassName("pop-module-sy");
        if (dom.length === 0) {
            let html = "<div class=\"pop-module-sy\" style=\"display: none; position: fixed; top:0; left: 0; width: 100%; height:100%;z-index: 100;\">\n" +
                "<div class=\"content\" style=\"width:10.84rem; height:1.44rem; line-height: 1.44rem; text-align: center; color: #ffffff;\n" +
                "background-color: rgba(0,0,0,0.6); font-size: 0.56rem; border-radius: 3px; margin: 50% auto 0;\">" + msg + "</div>\n</div>";
            $('body').append(html)
        } else {
            $('.pop-module-sy .content').html(msg);
        }
        $('.pop-module-sy').fadeIn(500);
        if (autoJs.config.toastDelayFunc)
            clearTimeout(autoJs.config.toastDelayFunc)
        autoJs.config.toastDelayFunc = setTimeout(function () {
            $('.pop-module-sy').fadeOut(500);
//        document.getElementsByClassName("pop-module-sy")[0].style.display = "none";
        }, !delay ? 2000 : delay);

    },

    /**
     * ajax请求
     * @param url
     * @param data  可选：请求数据对象
     * @param type  可选：请求类型，默认post
     * @param delay 可选：调用该函数之后，防止相同data和url的请求重复提交，只有当上一次请求成功或失败，才会发起新的请求
     */
    ajax: function (url, data, type, delay) {
        let _key;
        if (delay) {
            data = !data ? {} : data;
            _key = url + JSON.stringify(data)
            if (autoJs._data.ajaxArr.includes(_key)) {
                autoJs.toast("请求执行中，请勿重复点击~");
                return null;
            } else
                autoJs._data.ajaxArr.push(_key)
        }
        function delKey() {
            if (delay && _key) {
                let i = autoJs._data.ajaxArr.indexOf(_key);
                if (i > -1)
                    autoJs._data.ajaxArr.splice(i, 1)
            }
        }
        console.log('...进入')
        const dtd = $.Deferred(); //在函数内部，新建一个Deferred对象
        $.ajax({
            url: url,
            data: data,
            dataType: 'json',
            type: !type ? 'POST' : type,
            success: function (response) {
                delKey()
                console.log('...success')
                dtd.resolve(response);
            },
            error: function (XMLHttpRequest, textStatus) {
                delKey()
                console.log('...error')
                autoJs.toast('系统异常，加载数据出错~')
                dtd.reject(XMLHttpRequest, textStatus);
            }
        });
        return dtd.promise(); // 返回promise对象
    },

    /************************************** Message模块 ************************************/
     // 使用的前提是初始化的时候，传递了htmlSuccess/htmlError/htmlConfirm几个参数
    message: {
        success: function (data) {
            this.alert('success', data);
        },
        error: function (data) {
            this.alert('error', data);
        },
        confirm: function (data) {
            this.alert('confirm', data);
        },
        /**
         * 定制消息
         * @param data  自动替换的键值对
         * @param html  html模板
         * @param id    模块的id
         */
        custom: function (id, data, html) {
            autoJs.inner.showMessage(data, html, id)
        },

        alert: function (type, data) {
            let id, html;
            if (type === 'success') {
                id = autoJs.config.messageSuccessId;
                html = autoJs.config.htmlSuccess;
            } else if (type === 'error') {
                id = autoJs.config.messageErrorId;
                html = autoJs.config.htmlError;
            } else if (type === 'confirm') {
                id = autoJs.config.messageConfirmId;
                html = autoJs.config.htmlConfirm;
            } else {
                console.log('autoJs.message error: 不支持的弹框类型');
                return;
            }
            if (!html) {
                console.log('autoJs.message error: 未配置对应的', type, '类型html');
                return;
            }
            autoJs.inner.showMessage(data, html, id)
        }
    },

    /************************************** 基础工具 ************************************/
    utils: {
        /**
         * 格式化时间
         * @param fmt   数据格式
         * @param date  日期
         * @returns {*}
         */
        dateFormat: function (fmt, date) {
            if (!date)
                date = new Date();
            const o = {
                "M+": date.getMonth() + 1,                 //月份
                "d+": date.getDate(),                    //日
                "h+": date.getHours(),                   //小时
                "m+": date.getMinutes(),                 //分
                "s+": date.getSeconds(),                 //秒
                "q+": Math.floor((date.getMonth() + 3) / 3), //季度
                "S": date.getMilliseconds()             //毫秒
            };
            if (/(y+)/.test(fmt))
                fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (var k in o)
                if (new RegExp("(" + k + ")").test(fmt))
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            return fmt;
        },
        /**
         * 获取url拼接参数
         * @param name
         * @returns {string}
         */
        getQueryString: function (name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
            var r = window.location.search.substr(1).match(reg); //获取url中"?"符后的字符串并正则匹配
            var context = "";
            if (r != null)
                context = r[2];
            reg = null;
            r = null;
            return context == null || context == "" || context == "undefined" ? "" : context;
        },
        /**
         * 判断连个对象是否相同
         * @param a
         * @param b
         * @returns {boolean}
         */
        isObjectValueEqual: function (a, b) {
            // Of course, we can do it use for in
            // Create arrays of property names
            let aProps = Object.getOwnPropertyNames(a);
            let bProps = Object.getOwnPropertyNames(b);

            // If number of properties is different,
            // objects are not equivalent
            if (aProps.length !== bProps.length)
                return false;
            for (let i = 0; i < aProps.length; i++) {
                let propName = aProps[i];
                // If values of same property are not equal,
                // objects are not equivalent
                if (a[propName] !== b[propName]) {
                    return false;
                }
            }
            // If we made it this far, objects
            // are considered equivalent
            return true;
        },
        replaceCtx: function (text) {
            return text.replace(/\${request.contextPath}/g, autoJs.config.ctx);
        },
        // /**
        //  * 从arr中取出指定属性为指定值的对象
        //  * @param arr
        //  * @param key
        //  * @param val
        //  */
        // findObjInArr: function (arr, key, val) {
        //     if (!arr || !key || !val)
        //         return null;
        //     for (let i in arr)
        // },
        /**
         * 将textarea中包含换行信息的数据转成p标签，以供前端展示
         *
         * @param text
         * @returns {string}
         */
        textAreaToP: function (text) {
            let arr = text.split('\n');
            for (let i in arr)
                arr[i] = '<p>' + arr[i] + '</p>';
            return arr.join();
        },
        checkIdCard: function (code) {
            //身份证号合法性验证
            //支持15位和18位身份证号
            //支持地址编码、出生日期、校验位验证
            var city = {11: "北京", 12: "天津", 13: "河北", 14: "山西", 15: "内蒙古", 21: "辽宁", 22: "吉林", 23: "黑龙江 ", 31: "上海", 32: "江苏", 33: "浙江", 34: "安徽", 35: "福建", 36: "江西", 37: "山东", 41: "河南", 42: "湖北 ", 43: "湖南", 44: "广东", 45: "广西", 46: "海南", 50: "重庆", 51: "四川", 52: "贵州", 53: "云南", 54: "西藏 ", 61: "陕西", 62: "甘肃", 63: "青海", 64: "宁夏", 65: "新疆", 71: "台湾", 81: "香港", 82: "澳门", 91: "国外 "};
            var row = true;
            var msg = "验证成功";

            if (!code || !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/.test(code)) {
                row = false,
                    msg = "被保人身份证号格式错误";
            } else if (!city[code.substr(0, 2)]) {
                row = false,
                    msg = "被保人身份证号地址编码错误";
            } else {
                //18位身份证需要验证最后一位校验位
                if (code.length == 18) {
                    code = code.split('');
                    //∑(ai×Wi)(mod 11)
                    //加权因子
                    var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
                    //校验位
                    var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2];
                    var sum = 0;
                    var ai = 0;
                    var wi = 0;
                    for (var i = 0; i < 17; i++) {
                        ai = code[i];
                        wi = factor[i];
                        sum += ai * wi;
                    }
                    if (parity[sum % 11] != code[17].toUpperCase()) {
                        row = false,
                            msg = "被保人身份证号校验位错误";
                    }
                }
            }
            return row;
        },

        //获取滚动条当前的位置
        getScrollTop: function () {
            var scrollTop = 0;
            if (document.documentElement && document.documentElement.scrollTop) {
                scrollTop = document.documentElement.scrollTop;
            } else if (document.body) {
                scrollTop = document.body.scrollTop;
            }
            return scrollTop;
        },
        //获取当前可视范围的高度
        getClientHeight: function () {
            var clientHeight = 0;
            if(document.body.clientHeight && document.documentElement.clientHeight) {
                clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight);
            } else {
                clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
            }
            return clientHeight;
        },
        //获取文档完整的高度
        getScrollHeight: function () {
            return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
        }
    },
    /************************************** 内部方法 ************************************/
    // 不建议外部调用
    inner: {
        /**
         * 【内部方法】特殊情况的替换处理
         * @param type
         * @param $ele
         * @param $parent
         */
        specialHandle: function (type, $ele, $parent) {
            if ($parent.is('body'))
                return;
            console.log(type, $ele, $parent)
            if (type === '_close_btn')  // 关闭弹框的按钮绑定事件
                $ele.one('click', function () {
                    $parent.hide();
                })
        },
        /**
         * 【内部方法】显示消息的具体实现
         * @param data  自动替换的键值对
         * @param html  html模板
         * @param id    模块的id
         */
        showMessage: function (data, html, id) {
            let $element = $('#' + id);
            if ($element.length === 0) {
                $element = $(html);
                $element.attr('id', id);
                // $element.css('display', 'none');
                $element.hide();
                $('body').append($element)
            }
            autoJs.assignment(data, $element);
            // $element.css('display', 'block');
            $element.show();
        },
        /**
         * 根据配置是否打印日志，输出日志
         * @param msg   日志开头文本
         * @param data  如果需要打印obj对象，传递此参数
         */
        log: function () {
            if(autoJs.config.debug) {
                console.log(arguments)
            }
        }
    },

    /**
     * 获取带contexPath的路径，需要先配置ctx
     * @returns {string}
     */
    getHost: function () {
        return window.location.protocol + '//' + window.location.host + autoJs.config.ctx
    },

    _URL: function (uri) {
        return autoJs.getHost() + '/' + uri;
    },

    init: function (data) {
        $.extend(this.config, data)
    },


    _data: {
        ajaxArr: [],    // 当前在处理的需延迟校验的ajax请求
    },

    config: {
        /***** 可配置参数 ******/
        ctx: '', // contextPath

        htmlSuccess: '',  // 页面单独的弹窗成功html
        htmlError: '',  // 页面单独的弹窗失败html，展示提示
        htmlConfirm: '',    // 页面单独的确认框html

        debug: false, // 是否打印调试日志


        /***** 无需修改参数 ******/
        messageSuccessId: 'au_success',
        messageErrorId: 'au_error',
        messageConfirmId: 'au_confirm',
        toastDelayFunc: null,

    },
}

/***** js实现Map ******/
function MapBuilder(){var struct=function(key,value){this.key=key;this.value=value};var put=function(key,value){for(var i=0;i<this.arr.length;i++){if(this.arr[i].key===key){this.arr[i].value=value;return}}this.arr[this.arr.length]=new struct(key,value)};var get=function(key){for(var i=0;i<this.arr.length;i++){if(this.arr[i].key===key){return this.arr[i].value}}return null};var remove=function(key){var v;for(var i=0;i<this.arr.length;i++){v=this.arr.pop();if(v.key===key){continue}this.arr.unshift(v)}};var size=function(){return this.arr.length};var isEmpty=function(){return this.arr.length<=0};this.arr=new Array();this.get=get;this.put=put;this.remove=remove;this.size=size;this.isEmpty=isEmpty};